//
// Created by Alex on 2021/9/26.
//

#include "syntax_analysis.h"
#include <utility>
using namespace std;

inline lexical_parse syn_fsm::parse_at(int i) {
    return lex_res.parses[i];
}

syntax_result* syntax_analysis(lexical_result lexicalResult) {
    syntax_result* result =
            (new syn_fsm(std::move(lexicalResult)))->get_syntax_result();
    return result;
}

syn_fsm::syn_fsm(lexical_result _lex_res): lex_res(std::move(_lex_res)) {
    sym = lex_res.parses.begin();
    scope_stack = new vector<Scope*>();
    loop_stack = new vector<Stmt*>();
}

syntax_result* syn_fsm::get_syntax_result() {
    if (result) return result;
    error = new vector<Error>();
    CompUnit *comp_unit = get_comp_unit();
    result = new syntax_result(comp_unit, error);
    return result;
}

inline lexical_parse *syn_fsm::parse() {
    auto ret = new lexical_parse(*sym);
    sym++;
    curpos++;
    //printf("%d\n", ret->line_number);
    return ret;
}

CompUnit* syn_fsm::get_comp_unit() {
    auto decls = new vector<Decl*>();
    auto func_defs = new vector<FuncDef*>();
    MainFuncDef* main_func = nullptr;
    auto comp_unit = new CompUnit();
    scope_stack->push_back(comp_unit);
    while (sym != lex_res.parses.end()) {
        if (sym->text == "const") {
            auto const_decl = get_const_decl();
            decls->push_back(const_decl);
        } else {   // if (sym->text == "int" || sym->text == "void") {
            if ((sym + 2)->text == "(") {
                auto func_def = get_func_def();
                func_defs->push_back(func_def);
            } else {
                decls->push_back(get_var_decl());
            }
        }
    }
    for (auto e : *func_defs) {     // check for error type 'g'
        if (e->func_type->btype->text == "int") {
            if (e->block->block_items->empty()) {
                error->push_back(Error(e->block->rb->line_number, 'g'));
                continue;
            }
            auto bk = e->block->block_items->back();
            if (bk->obj_type != STMT || !((Stmt*)bk)->return_stmt) {
                error->push_back(Error(e->block->rb->line_number, 'g'));
            }
        }
    }
    main_func = new MainFuncDef(*(func_defs->rbegin()));
    func_defs->pop_back();
    comp_unit->add_content(decls, func_defs, main_func);
    scope_stack->pop_back();
    return comp_unit;
}

ConstDecl* syn_fsm::get_const_decl() {
    auto cnst_tk = parse();
    if (sym->text == "int") {
        auto int_tk = parse();
        auto const_def = get_const_def();
        if (!(*(scope_stack->rbegin()))->symtab->enter(new ValueSym(const_def))) {
            error->push_back(Error(const_def->ident->line_number, 'b'));
        }
        auto defs = new vector<pair<lexical_parse*, ConstDef*>*>();
        while (sym->text == ",") {
            auto comma = parse();
            auto def = get_const_def();
            if (!(*(scope_stack->rbegin()))->symtab->enter(new ValueSym(def))) {
                error->push_back(Error(def->ident->line_number, 'b'));
            }
            defs->push_back(new pair<lexical_parse*, ConstDef*>(comma, def));
        }
        if (sym->text == ";") {
            auto smc = parse();
            return new ConstDecl(cnst_tk, int_tk, const_def, defs, smc);
        } else {
            int line;
            if (defs->empty()) { line = const_def->get_line(); }
            else { line = (*(defs->rbegin()))->second->get_line(); }
            error->push_back(Error(line, 'i'));
            auto smc = new lexical_parse(line, ";", "SEMICN");
            return new ConstDecl(cnst_tk, int_tk, const_def, defs, smc);
        }
    } else {
        fprintf(stderr, "error in get_const_decl,  wrong type (not int)\n");
        exit(-1);
    }
}

VarDecl *syn_fsm::get_var_decl() {
    if (sym->text == "int") {
        auto int_tk = parse();
        auto var_def = get_var_def();
        if (!(*(scope_stack->rbegin()))->symtab->enter(new ValueSym(var_def))) {
            error->push_back(Error(var_def->ident->line_number, 'b'));
        }
        auto defs = new vector<pair<lexical_parse*, VarDef*>*>();
        while (sym->text == ",") {
            auto comma = parse();
            auto def = get_var_def();
            if (!(*(scope_stack->rbegin()))->symtab->enter(new ValueSym(def))) {
                error->push_back(Error(def->ident->line_number, 'b'));
            }
            defs->push_back(new pair<lexical_parse*, VarDef*>(comma, def));
        }
        if (sym->text == ";") {
            auto smc = parse();
            return new VarDecl(int_tk, var_def, defs, smc);
        } else {
            int line;
            if (defs->empty()) { line = var_def->get_line(); }
            else { line = (*(defs->rbegin()))->second->get_line(); }
            error->push_back(Error(line, 'i'));
            auto smc = new lexical_parse(line, ";", "SEMICN");
            return new VarDecl(int_tk, var_def, defs, smc);
        }
    } else {
        fprintf(stderr, "error in get_var_decl,  wrong type (not int)\n");
        exit(-1);
    }
}

//MainFuncDef *syn_fsm::get_main_func_def() {
//    proc_func_ret_type = INT;
//    auto int_tk = parse();
//    auto main_tk = parse();
//    auto lp = parse();
//    if (sym->text != ")") {
//        int line = lp->line_number;
//        error->push_back(Error(line, 'j'));
//        auto rp = new lexical_parse(line, ")", "RPARENT");
//        auto blk = get_block_normal();
//        return new MainFuncDef(int_tk, main_tk, lp, rp, blk);
//    }
//    auto rp = parse();
//    auto blk = get_block_normal();
//    return new MainFuncDef(int_tk, main_tk, lp, rp, blk);
//}

FuncDef *syn_fsm::get_func_def() {
    auto func_type = get_func_type();
    auto ident = parse();
    FuncDef *ret;
    auto lp = parse();
    if (sym->text != ")") {
        if (sym->text == "int") {
            auto params = get_func_fparams();
            lexical_parse *rp;
            if (sym->text != ")") {
                int line = params->get_line();
                error->push_back(Error(line, 'j'));
                rp = new lexical_parse(line, ")", "RPARENT");
            } else {
                rp = parse();
            }
            auto pvs = new vector<Symbol*>();
            for (auto e : *(params->real_fparams)) {
                pvs->push_back(new ValueSym(e));
            }
            ret = new FuncDef(func_type, ident, lp, params, rp);
            if (!(*(scope_stack->rbegin()))->symtab->enter(new FuncSym(ret))) {
                error->push_back(Error(ret->ident->line_number, 'b'));
            }
            auto blk = get_block_paramed(pvs);
            ret->add_content(blk);
            return ret;
        }
        int line = lp->line_number;
        error->push_back(Error(line, 'j'));
        auto rp = new lexical_parse(line, ")", "RPARENT");
        ret = new FuncDef(func_type, ident, lp, rp);
        if (!(*(scope_stack->rbegin()))->symtab->enter(new FuncSym(ret))) {
            error->push_back(Error(ret->ident->line_number, 'b'));
        }
        auto blk = get_block_normal();
        ret->add_content(blk);
        return ret;
    }
    auto rp = parse();
    ret = new FuncDef(func_type, ident, lp, rp);
    if (!(*(scope_stack->rbegin()))->symtab->enter(new FuncSym(ret))) {
        error->push_back(Error(ret->ident->line_number, 'b'));
    }
    auto blk = get_block_normal();
    ret->add_content(blk);
    return ret;
}

FuncType *syn_fsm::get_func_type() {
    auto btype = parse();
    proc_func_ret_type = (btype->text == "int") ?  INT : VOID;
    return new FuncType(btype);
}

FuncFParams *syn_fsm::get_func_fparams() {
    auto param = get_fparam();
    auto params = new vector<pair<lexical_parse*, FuncFParam*>*>();
    while (sym->text == ",") {
        auto comma = parse();
        auto par = get_fparam();
        params->push_back(new pair<lexical_parse*, FuncFParam*>(comma, par));
    }
    return new FuncFParams(param, params);
}

ConstDef *syn_fsm::get_const_def() {
    auto ident = parse();
    auto exps = new vector<tuple<lexical_parse*, ConstExp*, lexical_parse*>*>();
    while (sym->text == "[") {
        auto lp = parse();
        auto exp = get_const_exp();
        lexical_parse* rp;
        if (sym->text != "]") {
            int line = exp->get_line();
            error->push_back(Error(line, 'k'));
            rp = new lexical_parse(line, "]", "RBRACK");
        } else {
            rp = parse();
        }
        auto tup = new tuple<lexical_parse*, ConstExp*, lexical_parse*>(lp, exp, rp);
        exps->push_back(tup);
    }
    auto assign = parse();
    auto const_init_val = get_const_init_val();
    return new ConstDef(ident, exps, assign, const_init_val);
}

VarDef *syn_fsm::get_var_def() {
    auto ident = parse();
    auto exps = new vector<tuple<lexical_parse*, ConstExp*, lexical_parse*>*>();
    while (sym->text == "[") {
        auto lp = parse();
        auto exp = get_const_exp();
        lexical_parse* rp;
        if (sym->text != "]") {
            int line = exp->get_line();
            error->push_back(Error(line, 'k'));
            rp = new lexical_parse(line, "]", "RBRACK");
        } else {
            rp = parse();
        }
        auto tup = new tuple<lexical_parse*, ConstExp*, lexical_parse*>(lp, exp, rp);
        exps->push_back(tup);
    }
    if (sym->text == "=") {
        auto assign = parse();
        auto init_val = get_init_val();
        return new VarDef(ident, exps, assign, init_val);
    } else {
        return  new VarDef(ident, exps);
    }
}

FuncFParam *syn_fsm::get_fparam() {
    auto btype = parse();
    auto ident = parse();
    if (sym->text == "[") {
        auto lp = parse();
        lexical_parse* rp;
        if (sym->text != "]") {
            int line = lp->line_number;
            error->push_back(Error(line, 'k'));
            rp = new lexical_parse(line, "]", "RBRACK");
        } else {
            rp = parse();
        }
        auto exps = new vector<tuple<lexical_parse*, ConstExp*, lexical_parse*>*>();
        while (sym->text == "[") {
            auto e_lp = parse();
            auto exp = get_const_exp();
            lexical_parse* e_rp;
            if (sym->text != "]") {
                int line = exp->get_line();
                error->push_back(Error(line, 'k'));
                e_rp = new lexical_parse(line, "]", "RBRACK");
            } else {
                e_rp = parse();
            }
            auto tup = new tuple<lexical_parse*, ConstExp*, lexical_parse*>(e_lp, exp, e_rp);
            exps->push_back(tup);
        }
        return new FuncFParam(btype, ident, lp, rp, exps);
    } else {
        return new FuncFParam(btype, ident);
    }
}

Block *syn_fsm::get_block_normal() {
    auto ret = new Block(*(scope_stack->rbegin()));
    scope_stack->push_back(ret);
    auto lb = parse();
    auto items = new vector<BlockItem*>();
    while (sym->text != "}") {
        items->push_back(get_block_item());
    }
    auto rb = parse();
    ret->add_content(lb, items, rb);
    scope_stack->pop_back();
    return ret;
}

Block *syn_fsm::get_block_paramed(vector<Symbol*> *params) {
    auto ret = new Block((Scope*)(scope_stack->back()));
    scope_stack->push_back(ret);
    auto lb = parse();
    for (auto e : *params) {
        if (!(scope_stack->back()->symtab->enter(e))) {
            int line = -1;
            if (e->type == VALUESYM && ((ValueSym*)e)->is_param) {
                line = ((FuncFParam*)(e->def))->ident->line_number;
            } else {
                fprintf(stderr, "unknown type of sym when adding predefined params before line %d\n", lb->line_number);
            }
            error->push_back(Error(line, 'b'));
        }
    }
    auto items = new vector<BlockItem*>();
    while (sym->text != "}") {
        items->push_back(get_block_item());
    }
    auto rb = parse();
    ret->add_content(lb, items, rb);
    scope_stack->pop_back();
    return ret;
}

ConstExp *syn_fsm::get_const_exp() {
    return new ConstExp(get_add_exp());
}

ConstInitVal *syn_fsm::get_const_init_val() {
    if (sym->text == "{") {
        auto lb = parse();
        if (sym->text == "}") {
            auto rb = parse();
            return new ConstInitVal(lb, rb);
        } else {
            auto const_init_val = get_const_init_val();
            auto init_vals = new vector<pair<lexical_parse*, ConstInitVal*>*>();
            while (sym->text == ",") {
                auto comma = parse();
                auto c_init_val = get_const_init_val();
                auto pr = new pair<lexical_parse*, ConstInitVal*>(comma, c_init_val);
                init_vals->push_back(pr);
            }
            auto rb = parse();
            return new ConstInitVal(lb, const_init_val, init_vals, rb);
        }
    } else {
        return new ConstInitVal(get_const_exp());
    }
}

InitVal *syn_fsm::get_init_val() {
    if (sym->text == "{") {
        auto lb = parse();
        if (sym->text == "}") {
            auto rb = parse();
            return new InitVal(lb, rb);
        } else {
            auto init_val = get_init_val();
            auto init_vals = new vector<pair<lexical_parse*, InitVal*>*>();
            while (sym->text == ",") {
                auto comma = parse();
                auto iv = get_init_val();
                auto pr = new pair<lexical_parse*, InitVal*>(comma, iv);
                init_vals->push_back(pr);
            }
            auto rb = parse();
            return new InitVal(lb, init_val, init_vals, rb);
        }
    } else {
        return new InitVal(get_exp());
    }
}

BlockItem *syn_fsm::get_block_item() {
    if (sym->text == "int"){
        return get_var_decl();
    } else if (sym->text == "const") {
        return get_const_decl();
    } else {
        return get_stmt();
    }
}

Stmt* syn_fsm::get_stmt() {
    if (sym->text == "{") {
        return new Stmt(get_block_normal());
    } else if (sym->text == "if") {
        return new Stmt(get_if_stmt());
    } else if (sym->text == "while") {
        auto ret = new Stmt();
        loop_stack->push_back(ret);
        ret->add_content(get_while_stmt());
        loop_stack->pop_back();
        return ret;
    } else if (sym->text == "break") {
        return new Stmt(get_break_stmt());
    } else if (sym->text == "continue") {
        return new Stmt(get_continue_stmt());
    } else if (sym->text == "return") {
        return new Stmt(get_return_stmt());
    } else if (sym->text == "printf") {
        return new Stmt(get_printf_stmt());
    } else {
        //bool has_assign = false;
        //for (auto ts = sym; ts->text != ";"; ts++) {
        //    if (ts->text == "=") {
        //        has_assign = true;
        //        break;
        //    }
        //}
        //if (has_assign) {
        //    return new Stmt(get_assign_stmt());
        //} else {
        //    return new Stmt(get_selective_exp());
        //}
        if (sym->type_code == "IDENFR") {
            int pos = pre_read_lval(curpos);
            if ((sym + pos)->text == "=") {
                return new Stmt(get_assign_stmt());
            } else {
                return new Stmt(get_selective_exp());
            }
        } else {
            return new Stmt(get_selective_exp());
        }
    }
}

// iter + ret => the next sym to read, '=' if to assign
int syn_fsm::pre_read_lval(int start) {
    if (parse_at(start).type_code != "IDENFR")
        return -1;
    if (parse_at(start + 1).text == "(")
        return -1;
    int pos = 1;
    while (parse_at(start + pos).text == "[") {
        pos++;
        pos += pre_read_exp(start + pos);
        if (parse_at(start + pos).text == "]") {
            pos++;
        }
    }
    return pos;
}

int syn_fsm::pre_read_exp(int start) {
    return pre_read_add_exp(start);
}

int syn_fsm::pre_read_add_exp(int start) {
    int pos = pre_read_mul_exp(start);
    while (parse_at(start + pos).text == "+" || parse_at(start + pos).text == "-") {
        pos++;
        pos += pre_read_mul_exp(start + pos);
    }
    return pos;
}

int syn_fsm::pre_read_mul_exp(int start) {
    int pos = pre_read_unary_exp(start);
    while (parse_at(start + pos).text == "*" || parse_at(start + pos).text == "/" ||
    parse_at(start + pos).text == "%") {
        pos++;
        pos += pre_read_unary_exp(start + pos);
    }
    return pos;
}

int syn_fsm::pre_read_unary_exp(int start) {
    if (parse_at(start).type_code == "IDENFR" && parse_at(start + 1).text == "(") {
        if (parse_at(start + 2).text == ")") {
            return 3;
        }
        if (is_exp(start + 2)) {
            int pos = 2 + pre_read_func_rparams(start + 2);
            if (parse_at(start + pos).text == ")") {
                return pos + 1;
            } else {
                return pos;
            }
        } else {
            return 2;
        }
    } else if (parse_at(start).text == "+" || parse_at(start).text == "-" || parse_at(start).text == "!") {
        return 1 + pre_read_unary_exp(start + 1);
    } else {
        return pre_read_primary_exp(start);
    }
}

int syn_fsm::pre_read_func_rparams(int start) {
    int pos = pre_read_exp(start);
    while (parse_at(start + pos).text == ",") {
        pos++;
        pos += pre_read_exp(start + pos);
    }
    return pos;
}

int syn_fsm::pre_read_primary_exp(int start) {
    if (parse_at(start).text == "(") {
        return 2 + pre_read_unary_exp(start + 1);
    } else if (parse_at(start).type_code == "INTCON") {
        return 1;
    } else {
        return pre_read_lval(start);
    }
}

AddExp* syn_fsm::get_add_exp() {
    auto mul_exp = get_mul_exp();
    auto top = new AddExp(mul_exp);
    if (sym->text != "+" && sym->text != "-") {
        return top;
    }
    while (sym->text == "+" || sym->text == "-") {
        auto op = parse();
        auto rmul_exp = get_mul_exp();
        top = new AddExp(top, op, rmul_exp);
    }
    return top;
}

Exp *syn_fsm::get_exp() {
    return new Exp(get_add_exp());
}

IfStmt *syn_fsm::get_if_stmt() {
    auto if_tk = parse();
    auto lp = parse();
    auto cond = get_cond();
    lexical_parse* rp;
    if (sym->text != ")") {
        int line = cond->get_line();
        error->push_back(Error(line, 'j'));
        rp = new lexical_parse(line, ")", "RPARENT");
    } else {
        rp = parse();
    }
    auto if_true = get_stmt();
    if (sym->text == "else") {
        auto else_tk = parse();
        return new IfStmt(if_tk, lp, cond, rp, if_true, else_tk, get_stmt());
    } else {
        return new IfStmt(if_tk, lp, cond, rp, if_true);
    }
}

WhileStmt *syn_fsm::get_while_stmt() {
    auto while_tk = parse();
    auto lp = parse();
    auto cond = get_cond();
    lexical_parse* rp;
    if (sym->text != ")") {
        int line = cond->get_line();
        error->push_back(Error(line, 'j'));
        rp = new lexical_parse(line, ")", "RPARENT");
    } else {
        rp = parse();
    }
    return new WhileStmt(while_tk, lp, cond, rp, get_stmt());
}

BreakStmt *syn_fsm::get_break_stmt() {
    auto tk = parse();
    Stmt *outer = nullptr;
    lexical_parse *smc;
    if (loop_stack->empty()) {
        error->push_back(Error(tk->line_number, 'm'));
    } else {
        outer = loop_stack->back();
    }
    if (sym->text == ";") {
        smc = parse();
    } else {
        int line = tk->line_number;
        smc = new lexical_parse(line, ";", "SEMICN");
        error->push_back(Error(line, 'i'));
    }
    return new BreakStmt(tk, smc, outer);
}

ContinueStmt *syn_fsm::get_continue_stmt() {
    auto tk = parse();
    Stmt *outer = nullptr;
    lexical_parse *smc;
    if (loop_stack->empty()) {
        error->push_back(Error(tk->line_number, 'm'));
    } else {
        outer = loop_stack->back();
    }
    if (sym->text == ";") {
        smc = parse();
    } else {
        int line = tk->line_number;
        smc = new lexical_parse(line, ";", "SEMICN");
        error->push_back(Error(line, 'i'));
    }
    return new ContinueStmt(tk, smc, outer);
}

ReturnStmt *syn_fsm::get_return_stmt() {
    auto tk = parse();
    lexical_parse *smc;
    if (sym->text == ";") {
        return new ReturnStmt(tk, parse());
    } else {
        if (is_exp(curpos)) {
            if (proc_func_ret_type == VOID) {
                error->push_back(Error(tk->line_number, 'f'));
            }
            auto exp = get_exp();
            if (sym->text == ";") {
                smc = parse();
            } else {
                int line = exp->get_line();
                smc = new lexical_parse(line, ";", "SEMICN");
                error->push_back(Error(line, 'i'));
            }
            return new ReturnStmt(tk, exp, smc);
        } else {
            if (sym->text == ";") {
                smc = parse();
            } else {
                int line = tk->line_number;
                smc = new lexical_parse(line, ";", "SEMICN");
                error->push_back(Error(line, 'i'));
            }
            return new ReturnStmt(tk, smc);
        }
    }
}

PrintfStmt *syn_fsm::get_printf_stmt() {
    auto tk = parse();
    auto lp = parse();
    auto fmt = parse();
    if (!check_fmt(fmt)) {
        error->push_back(Error(fmt->line_number, 'a'));
    }
    auto exps = new vector<pair<lexical_parse*, Exp*>*>();
    while (sym->text == ",") {
        auto comma = parse();
        auto exp = get_exp();
        auto pr = new pair<lexical_parse*, Exp*>(comma, exp);
        exps->push_back(pr);
    }
    lexical_parse* rp;
    if (sym->text != ")") {
        int line = fmt->line_number;
        error->push_back(Error(line, 'j'));
        rp = new lexical_parse(line, ")", "RPARENT");
    } else {
        rp = parse();
    }
    lexical_parse *smc;
    if (sym->text == ";") {
        smc = parse();
    } else {
        int line = rp->line_number;
        smc = new lexical_parse(line, ";", "SEMICN");
        error->push_back(Error(line, 'i'));
    }
    // check param num
    int pd_num = 0;
    for (int i = 0; i < fmt->text.size(); i++) {
        if (fmt->text[i] == '%') {
            i++;
            if (fmt->text[i] == 'd') {
                pd_num++;
            }
        }
    }
    if (pd_num != exps->size()) {
        error->push_back(Error(tk->line_number, 'l'));
    }
    return new PrintfStmt(tk, lp, fmt, exps, rp, smc);
}

AssignStmt* syn_fsm::get_assign_stmt() {
    auto lval = get_lval();
    if (lval->value_sym && lval->value_sym->is_const) {
        error->push_back(Error(lval->ident->line_number, 'h'));
    }
    auto assign = parse();
    lexical_parse *smc;
    if (sym->text == "getint") {
        auto tk = parse();
        auto lp = parse();
        lexical_parse* rp;
        if (sym->text != ")") {
            int line = lp->line_number;
            error->push_back(Error(line, 'j'));
            rp = new lexical_parse(line, ")", "RPARENT");
        } else {
            rp = parse();
        }
        if (sym->text == ";") {
            smc = parse();
        } else {
            int line = rp->line_number;
            error->push_back(Error(line, 'i'));
            smc = new lexical_parse(line, ";", "SEMICN");
        }
        return new AssignStmt(lval, assign, tk, lp, rp, smc);
    } else {
        auto exp = get_exp();
        if (sym->text == ";") {
            smc = parse();
        } else {
            int line = exp->get_line();
            error->push_back(Error(line, 'i'));
            smc = new lexical_parse(line, ";", "SEMICN");
        }
        return new AssignStmt(lval, assign, exp, smc);
    }
}

SelectiveExp* syn_fsm::get_selective_exp() {
    if (sym->text == ";") {
        return new SelectiveExp(parse());
    } else {
        auto exp = get_exp();
        lexical_parse *smc;
        if (sym->text == ";") {
            smc = parse();
        } else {
            int line = exp->get_line();
            error->push_back(Error(line, 'i'));
            smc = new lexical_parse(line, ";", "SEMICN");
        }
        return new SelectiveExp(exp, smc);
    }
}

MulExp* syn_fsm::get_mul_exp() {
    auto unary_exp = get_unary_exp();
    auto top = new MulExp(unary_exp);
    if (sym->text != "*" && sym->text != "/" && sym->text != "%") {
        return top;
    }
    while (sym->text == "*" || sym->text == "/" || sym->text == "%") {
        auto op = parse();
        auto r_exp = get_unary_exp();
        top = new MulExp(top, op, r_exp);
    }
    return top;
}

Cond* syn_fsm::get_cond() {
    return new Cond(get_lor_exp());
}

UnaryExp* syn_fsm::get_unary_exp() {
    if (sym->type_code == "IDENFR" && (sym + 1)->text == "(") {
        auto ident = parse();
        auto func_sym = (FuncSym*)(*(scope_stack->rbegin()))->lookup(ident->text, FUNCSYM);
        if (!func_sym) {
            error->push_back(Error(ident->line_number, 'c'));
        }
        int real_fparam_dim = 0;
        vector<FuncFParam*> * rfp = nullptr;
        if (func_sym) {
            if (!((FuncDef*)(func_sym->def))->func_fparams) {
                real_fparam_dim = 0;
            } else {
                rfp = ((FuncDef*)(func_sym->def))->func_fparams->real_fparams;
                real_fparam_dim = (int)(rfp->size());
            }
        }
        auto lp = parse();
        if (sym->text == ")") {
            auto rp = parse();
            if (real_fparam_dim > 0) {
                error->push_back(Error(ident->line_number, 'd'));
            }
            return new UnaryExp(ident, lp, rp, func_sym);
        } else {
            if (is_exp(curpos)) {
                auto params = get_func_rparams();
                auto rrp = params->real_exps;
                if (func_sym) {
                    if (rrp->size() != real_fparam_dim) {
                        error->push_back(Error(ident->line_number, 'd'));
                    } else {     // check type
                        for (int i = 0; i < real_fparam_dim; i++) {
                            int dr = (*rrp)[i]->get_dimension();
                            // if (dr < 0) continue;
                            int df = (*rfp)[i]->get_dimension();
                            if (dr != df) {
                                error->push_back(Error(ident->line_number, 'e'));
                            }
                        }
                    }
                }
                lexical_parse* rp;
                if (sym->text != ")") {
                    int line = params->get_line();
                    error->push_back(Error(line, 'j'));
                    rp = new lexical_parse(line, ")", "RPARENT");
                } else {
                    rp = parse();
                }
                return new UnaryExp(ident, lp, params, rp, func_sym);
            } else {
                int line = lp->line_number;
                error->push_back(Error(line, 'j'));
                auto rp = new lexical_parse(line, ")", "RPARENT");
                if (func_sym) {
                    if (real_fparam_dim > 0) {
                        error->push_back(Error(ident->line_number, 'd'));
                    }
                }
                return new UnaryExp(ident, lp, rp, func_sym);
            }
        }
    } else if (sym->text == "+" || sym->text == "-" || sym->text == "!") {
        auto op = get_unary_op();
        auto exp = get_unary_exp();
        return new UnaryExp(op, exp);
    } else {
        return new UnaryExp(get_primary_exp());
    }
}

LVal* syn_fsm::get_lval() {
    auto ident = parse();
    auto value_sym = (ValueSym*)(*(scope_stack->rbegin()))->lookup(ident->text, VALUESYM);
    if (!value_sym) {
        error->push_back(Error(ident->line_number, 'c'));
    }
    auto exps = new vector<tuple<lexical_parse*,Exp*, lexical_parse*>*>();
    while (sym->text == "[") {
        auto lp = parse();
        auto exp = get_exp();
        lexical_parse* rp;
        if (sym->text != "]") {
            int line = exp->get_line();
            error->push_back(Error(line, 'k'));
            rp = new lexical_parse(line, "]", "RBRACK");
        } else {
            rp = parse();
        }
        auto tp = new tuple<lexical_parse*,Exp*, lexical_parse*>(lp,exp, rp);
        exps->push_back(tp);
    }
    return new LVal(ident, exps, value_sym);
}

LOrExp* syn_fsm::get_lor_exp() {
    auto l_exp = get_land_exp();
    auto top = new LOrExp(l_exp);
    if (sym->text != "||") {
        return top;
    }
    while (sym->text == "||") {
        auto op = parse();
        auto r_exp = get_land_exp();
        top = new LOrExp(top, op, r_exp);
    }
    return top;
}

PrimaryExp* syn_fsm::get_primary_exp() {
    if (sym->text == "(") {
        auto lp = parse();
        auto exp = get_exp();
        lexical_parse *rp;
        if (sym->text != ")") {
            int line = exp->get_line();
            rp = new lexical_parse(line, ")", "RPARENT");
            error->push_back(Error(line, 'j'));
        } else {
            rp = parse();
        }
        return new PrimaryExp(lp, exp, rp);
    } else if (sym->type_code == "INTCON") {
        return new PrimaryExp(get_number());
    } else {
        return new PrimaryExp(get_lval());
    }
}

FuncRParams* syn_fsm::get_func_rparams() {
    auto exp = get_exp();
    auto exps = new vector<pair<lexical_parse*, Exp*>*>();
    while (sym->text == ",") {
        auto comma = parse();
        auto ee = get_exp();
        auto pr = new pair<lexical_parse*, Exp*>(comma, ee);
        exps->push_back(pr);
    }
    return new FuncRParams(exp, exps);
}

UnaryOp* syn_fsm::get_unary_op() {
    return new UnaryOp(parse());
}

LAndExp* syn_fsm::get_land_exp() {
    auto l_exp = get_eq_exp();
    auto top = new LAndExp(l_exp);
    if (sym->text != "&&") {
        return top;
    }
    while (sym->text == "&&") {
        auto op = parse();
        auto r_exp = get_eq_exp();
        top = new LAndExp(top, op, r_exp);
    }
    return top;
}

Number* syn_fsm::get_number() {
    return new Number(parse());
}

EqExp* syn_fsm::get_eq_exp() {
    auto l_exp = get_rel_exp();
    auto top = new EqExp(l_exp);
    if (sym->text != "==" && sym->text != "!=") {
        return top;
    }
    while (sym->text == "==" || sym->text == "!=") {
        auto op = parse();
        auto r_exp = get_rel_exp();
        top = new EqExp(top, op, r_exp);
    }
    return top;
}

RelExp* syn_fsm::get_rel_exp() {
    auto l_exp = get_add_exp();
    auto top = new RelExp(l_exp);
    if (sym->text != ">" && sym->text != "<" &&
    sym->text != ">=" && sym->text != "<=") {
        return top;
    }
    while (sym->text == ">" || sym->text == "<" ||
    sym->text == ">=" || sym->text == "<=") {
        auto op = parse();
        auto r_exp = get_add_exp();
        top = new RelExp(top, op, r_exp);
    }
    return top;
}

int ConstDef::get_line() const { return const_init_val->get_line(); }

int ConstInitVal::get_line() const {
    if (const_exp) { return const_exp->get_line(); }
    return rb->line_number;
}

int ConstExp::get_line() const { return add_exp->get_line(); }

int AddExp::get_line() const { return mul_exp->get_line(); }

int MulExp::get_line() const { return unary_exp->get_line(); }

int UnaryExp::get_line() const {
    if (primary_exp) { return primary_exp->get_line(); }
    if (rp) { return rp->line_number; }
    return unary_exp->get_line();
}

int PrimaryExp::get_line() const {
    if (rp) { return rp->line_number; }
    if (lval) { return lval->get_line(); }
    return number->get_line();
}

int LVal::get_line() const {
    if (exps->empty()) { return ident->line_number; }
    return (get<2>(**(exps->rbegin())))->line_number;
}

int Number::get_line() const { return int_const->line_number; }

int VarDef::get_line() const {
    if (init_val) { return init_val->get_line(); }
    if (exps->empty()) { return ident->line_number; }
    return (get<2>(**(exps->rbegin())))->line_number;
}

int InitVal::get_line() const {
    if (exp) { return exp->get_line(); }
    return rb->line_number;
}

int Exp::get_line() const { return add_exp->get_line(); }

bool syn_fsm::is_exp(int start) {
    return parse_at(start).type_code == "IDENFR" || parse_at(start).text == "+" || parse_at(start).text == "-"
    || parse_at(start).text == "!" || parse_at(start).text == "(" || parse_at(start).type_code == "INTCON";
}

bool syn_fsm::check_fmt(lexical_parse *fmt) {
    for (int i = 1; i < fmt->text.size() - 1; i++) {
        char c = fmt->text[i];
        if (c == 32 || c == 33) continue;
        if (40 <= c && c <= 126) {
            if (c == '\\') {
                c = fmt->text[++i];
                if(c == 'n') continue;
                else return false;
            }
            continue;
        }
        if (c == '%') {
            c = fmt->text[++i];
            if(c == '%' || c == 'd') continue;
            else return false;
        }
        return false;
    }
    return true;
}

int FuncFParams::get_line() const {
    if (func_fparams->empty()) return func_fparam->get_line();
    return get<1>(**(func_fparams->rbegin()))->get_line();
}

int FuncFParam::get_line() const {
    if (!rb) return ident->line_number;
    if (exps->empty()) return rb->line_number;
    return get<2>(**(exps->rbegin()))->line_number;
}

int Cond::get_line() const { return lor_exp->get_line(); }

int LOrExp::get_line() const { return land_exp->get_line(); }

int LAndExp::get_line() const { return eq_exp->get_line(); }

int EqExp::get_line() const { return rel_exp->get_line(); }

int RelExp::get_line() const { return add_exp->get_line(); }

int FuncRParams::get_line() const { return exp->get_line(); }